fix: the update card stops demanding a redeploy the edge just hasn't caught up with - #34
Merged
Merged
Conversation
…t caught up with A deploy returns when Cloudflare's API accepts the new Worker, but the edge keeps serving the previous one for seconds — occasionally longer. During that window the relay's /api/health still answers with the old FLUE_VERSION stamp, so Status(), which compared deployStamp() against that live read, put the "update the relay" card right back under the checkmarks the deploy just earned. The user obliges and redeploys identical bytes; time was the actual fix. The daemon knows what it just shipped — stop throwing that away. relayUIService now remembers (origin, stamp) after a successful Provision or Update, and Status answers from that memory instead of the health read, but only while the memory stays true: a failed deploy sets nothing, a binary whose own deployStamp changed falls back to the health read so a genuinely newer build still gets its card, and the memory is keyed to the origin it shipped to, so SetAddress (or a re-join under a running daemon) self-invalidates it. A daemon restart drops the memory, deliberately — propagation is long done by then. The CLI paths (flue relay setup / update) run in their own process, print, and exit; they never serve Status, so they need no memory. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The repro
Deploy the relay from the Remote screen. Every checkmark lands — token verified, worker deployed, web app uploaded — and the card underneath still says "update the relay." Click it, and the second deploy is a byte-for-byte no-op. Wait a minute instead, and the card quiets down on its own. Two deploys happened in the maintainer's repro; time was the actual fix.
The mechanism
The card compares this daemon's
deployStamp()(release version, or on dev builds a content hash of the Worker module + web assets) againstdeployedVersion(), which does a liveGET <origin>/api/healthand returns theFLUE_VERSIONthe currently-serving Worker reports. A deploy returns when the Cloudflare API accepts the new Worker — but the edge keeps serving the previous one for seconds, occasionally longer. During that propagation window/api/healthstill answers with the old stamp, soStatus()faithfully reports a version skew that is really just replication lag, and the UI offers an update that already happened.What changed
The daemon knows what it just shipped — it stops throwing that away.
relayUIServicenow remembers(origin, stamp)after a successfulProvisionorUpdate, andStatus()answers from that memory instead of the transient health read — but only while the memory stays true:deployStamp()changed (rebuilt/upgraded daemon) makes the memory stale — the health read wins again, so a genuinely newer build still shows its update card. Within one process the stamp is constant, so this guard mostly states the invariant; it costs one comparison.SetAddress(or arelay joinunder a running daemon) movescfg.Origin, and the memory self-invalidates rather than vouching for a relay this process never deployed to — the Worker behind a repointed name should be the same one, but that's the health read's fact to confirm, not memory's to assume.SetAddressitself needed no code change; its comment now says the interaction is deliberate.The memory lives under its own mutex, not the deploy-serialising
mu, soStatus()renders during a deploy instead of queueing behind one. TheRelayUIStatus.DeployedVersiondoc-comment ininternal/daemon/relayui.gowas updated to stay truthful.The CLI paths need nothing.
flue relay setupandflue relay updateare their own process: they deploy, print the checkmarks, and exit — they never serveStatus(), so there is no card for them to mislead. They cannot populate the daemon's memory either (different process), which means a CLI update followed within seconds by opening the Remote screen can still see the lag — a much narrower window, self-healing, and not worth a persistence mechanism whose staleness would then need managing.One accepted trade-off, stated plainly: while the memory holds, this daemon does no live health checks — so another machine deploying a different version to the same Worker mid-lifetime goes unnoticed until this daemon restarts or rebuilds. That's the rare case, and a wrong "up to date" there is strictly less harmful than the wrong "update me" this fixes for every deploy.
Test evidence
Three new tests in
cmd/flue/relayui_test.go, driven through the same fake Cloudflare and a health server that answers the previous stamp (what a real mid-propagation edge does):TestRelayUIStatusTrustsItsOwnDeployWhileTheEdgeCatchesUp— before the deploy the health read decides (the card shows); after a successfulUpdateand after aProvision,Statusreports this binary's stamp even though the edge still answers the old one.TestRelayUIFailedUpdateLeavesTheHealthReadInCharge— a deploy the API refused earns no memory; the card logic is unchanged.TestRelayUIStaleShipMemoryLosesToTheHealthRead— a planted stamp mismatch (the rebuilt-binary shape) falls back to the health read; a realSetAddressrepoint leaves neither memory nor health speaking for the new origin.🤖 Generated with Claude Code